package com.my.config;

import java.awt.print.Book;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import com.my.annotation.Model;
import com.my.app.code.CodeCreator;
import com.my.app.code.CodeCreatorProperties;
import com.my.framework.dao.BaseDao;
import com.my.toolkit.template.TemplateUtil;
import com.my.utils.ClassUtil;

import lombok.extern.slf4j.Slf4j;

@org.springframework.context.annotation.Configuration
@Slf4j
public class MybatisAutoConfiguration {
	
	private final MybatisProperties properties;

	private final Interceptor[] interceptors;

	private final ResourceLoader resourceLoader;

	private final DatabaseIdProvider databaseIdProvider;

	private final List<ConfigurationCustomizer> configurationCustomizers;
	
	private CodeCreator codeCreator;

	public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,
			ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider,
			ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider,CodeCreator codeCreator) {
		this.properties = properties;
		this.interceptors = interceptorsProvider.getIfAvailable();
		this.resourceLoader = resourceLoader;
		this.databaseIdProvider = databaseIdProvider.getIfAvailable();
		this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
		this.codeCreator=codeCreator;
	}

	@Bean
	@Order
	public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
		SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
		factory.setDataSource(dataSource);
		factory.setVfs(SpringBootVFS.class);
		if (StringUtils.hasText(this.properties.getConfigLocation())) {
			factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
		}
		Configuration configuration = this.properties.getConfiguration();
		if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
			configuration = new Configuration();
		}
		if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
			for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
				customizer.customize(configuration);
			}
		}
		factory.setConfiguration(configuration);
		if (this.properties.getConfigurationProperties() != null) {
			factory.setConfigurationProperties(this.properties.getConfigurationProperties());
		}
		if (!ObjectUtils.isEmpty(this.interceptors)) {
			factory.setPlugins(this.interceptors);
		}
		if (this.databaseIdProvider != null) {
			factory.setDatabaseIdProvider(this.databaseIdProvider);
		}
		if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
			factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
		}
		if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
			factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
		}

		factory.setMapperLocations(getMapperLoaction());

		return factory.getObject();
	}

	private Resource[] getMapperLoaction() throws Exception {
		Resource[] configMapper = this.properties.resolveMapperLocations();
		List<Resource> all = new ArrayList();
		if (!ObjectUtils.isEmpty(configMapper)) {
			all.addAll(Arrays.asList(configMapper));
		}

		PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
		String base=codeCreator.getBasePackage().replace(".", "/");
		Resource[] allClass = resolver.getResources("classpath*:"+base+"/**/*.class");
		for (int i = 0; i < allClass.length; i++) {
			Resource res = allClass[i];
			try {
				String xml=createMapperXml(res);
				if(xml==null) {
					continue;
				}
				ByteArrayResource bs=new ByteArrayResource(xml.getBytes(),"UTF-8");
				all.add(bs);
			}catch(Exception ex) {
				log.warn("自动生成"+res+"的映射出错:"+ex.getMessage(),ex);
			}
		}
		return all.toArray(configMapper);
	}

	private String createMapperXml(Resource res) throws Exception {
		Class daoClass=getDaoClass(res);
		if(daoClass==null) {
			return null;
		}
		return codeCreator.getMapperXml(daoClass);
	}
	
	
	

	/**
	 * 获取和数据库对应的dao类
	 * 1.查找所有prefix包下的类
	 * 2.选出继承BaseDao的
	 * 3.配置类Mapper的
	 */
	private Class getDaoClass( Resource res) throws IOException, ClassNotFoundException {
		String prefix=codeCreator.getBasePackage().replace(".", "/")+"/";
		String url = res.getURL().getPath();
		int beginIndex = url.lastIndexOf(prefix);
		if (beginIndex == -1) {
			return null;
		}
		int end = url.indexOf(".", beginIndex);
		String className=url.substring(beginIndex, end).replace("/", ".");
		Class daoClass = Class.forName(className);
		if(daoClass==BaseDao.class) {
			return null;
		}
		
		if (!ClassUtil.isInstance(daoClass, BaseDao.class)) {
			return null;
		}
		Mapper mapper=AnnotationUtils.getAnnotation(daoClass, Mapper.class);
		if(mapper==null) {
			return null;
		}
		
		return daoClass;
	}
	
	
	
}
